fix(actions): async DB compatibility for 5 plugins#109
Conversation
Replace fragile version-string check (_owui_version_ge) with runtime coroutine detection (iscoroutinefunction + asyncio.iscoroutine) in the _call_db helper. In isolated filter/action sandboxes, importing open_webui.env.VERSION can fail and fall back to "0.0.0", causing async DB methods (Users.get_user_by_id, Chats.get_chat_by_id, Models.get_model_by_id) to be called synchronously on OWUI >= 0.9.0 and raising "TypeError: object coroutine can't be used in 'await'". Two bug patterns addressed: - Class A (6 files): existing _call_db helper using version-string check, switched to iscoroutinefunction. export_to_docx (export_to_word.py, export_to_word_cn.py) smart-mind-map (smart_mind_map.py) infographic (infographic.py) export_to_excel (export_to_excel.py, export_to_excel_cn.py) - Class B (2 files): no _call_db helper, called Users.get_user_by_id() synchronously. Added helper and wrapped call. flash-card (flash_card.py, flash_card_cn.py) Version bumps and bilingual release notes for all 5 plugins. Index files and docs version-badges updated. Refs: #101, #107
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
✅ Plugin Version Check / 插件版本检查版本更新检测通过!PR 包含版本变化和更新说明。 Version check passed! PR contains version changes and update description. 版本变化 / Version ChangesPlugin Updates
This comment was generated automatically. / 此评论由自动生成。 |
There was a problem hiding this comment.
Code Review
本次拉取请求主要更新了多个 OpenWebUI 插件(包括 Word 导出、Excel 导出、闪记卡、智能信息图和思维导图),旨在解决 OpenWebUI >= 0.9.0 / v0.10.1 上的异步数据库兼容性问题。修改中引入或优化了 _call_db 辅助函数,通过 iscoroutinefunction 和 asyncio.iscoroutine 进行运行时协程检测,从而替代了此前不稳定的版本号判断。审查意见指出,仅使用 asyncio.iscoroutine(res) 无法检测到其他可等待对象(如 Future、Task 或自定义 awaitable),建议将其替换为 inspect.isawaitable(res) 以提升代码的健壮性与前瞻性。
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
| if asyncio.iscoroutine(res): | ||
| return await res |
There was a problem hiding this comment.
Using asyncio.iscoroutine(res) only detects coroutine objects created by async def functions. It will return False for other awaitable objects like asyncio.Future, asyncio.Task, or custom awaitables (objects implementing __await__).
To make this helper fully robust and future-proof against any awaitable database responses, use inspect.isawaitable(res) instead.
Note: Remember to update the import at the top of the file to:
from inspect import iscoroutinefunction, isawaitable| if asyncio.iscoroutine(res): | |
| return await res | |
| if isawaitable(res): | |
| return await res |
Summary
Unifies the async DB compatibility fix across 5 action plugins that have
openwebui_idmetadata. Follows the same approach as #107 (async-context-compression) — replace fragile version-string detection with runtime coroutine detection.Background
OWUI >= 0.9.0 turned
Users.get_user_by_id,Chats.get_chat_by_id,Models.get_model_by_idinto coroutines. The previous_owui_version_ge("0.9.0")guard is unreliable in isolated filter/action sandboxes:from open_webui.env import VERSIONcan fail and fall back to"0.0.0", causing async methods to be called synchronously and raisingTypeError: object coroutine can't be used in 'await'. Originally reported in #101.Fix
_call_dbnow usesiscoroutinefunction(method)+asyncio.iscoroutine(res)instead of version-string comparison:Two bug patterns
Class A — existing
_call_dbusing version-string check (6 files):export_to_docx—export_to_word.py,export_to_word_cn.pysmart-mind-map—smart_mind_map.pyinfographic—infographic.pyexport_to_excel—export_to_excel.py,export_to_excel_cn.pyClass B — no
_call_dbhelper, calledUsers.get_user_by_id()synchronously (2 files):flash-card—flash_card.py,flash_card_cn.py(added helper + wrapped call)Version bumps
Docs
v{version}.md+v{version}_CN.md) for all 5 pluginsdocs/plugins/actions/index.mdandindex.zh.mdversion fields updatedsmart-infographic.md,smart-infographic.zh.md,export-to-word.md,export-to-word.zh.mdbumpedNote on check_version_consistency.py
The script reports 2 false positives (
export_to_excel_cn.pyandexport_to_word_cn.py) because its regexversion:also matchesrequired_open_webui_version: 0.10.2. Running--fixwould corrupt that line, so I left the script alone. The actualversion:metadata in all 8 .py files is correct.Refs: #101, #107